<template>
  <el-select :disabled="disabled" :multiple="multiple" :multiple-limit="limitCount" :clearable="clearable"
             :filter-method="onSearch" v-bind="$attrs" filterable value-key="value" v-model="curValue">
    <div v-if="isPage" class="x-p-5">
      <el-input v-model="search">
        <template #append>
          <el-button @click="onPageSearch(1)" :icon="Search"/>
        </template>
      </el-input>
    </div>

    <div style="max-height: 300px;overflow-y: auto;">
      <el-option :selected="true" label="全部" value="" v-if="isDefault"></el-option>

      <el-option v-for="(v, index) in selected" :key="v" :label="v.label" :value="v.value"></el-option>

      <template v-if="! $emptyArray(list)" v-for="(item, index) in list">
        <template v-if="multiple ? curValue.indexOf(item.value) === -1 : item.value != curValue">
        <el-option :key="item.value"
                   :value="item.value" :label="item.label"></el-option>
        </template>
      </template>
      <template v-else>
        <el-option value="" :disabled="true">
          <div style="width: 100%;text-align: center">
            暂无数据
          </div>
        </el-option>
      </template>
    </div>

    <div v-if="isPage" class="x-row-reverse">
      <el-pagination @current-change="onPageSearch" :page-size="pagination.pageSize" layout="prev, pager, next" :total="pagination.total" :current-page="pagination.pageNo"/>
    </div>
  </el-select>
</template>

<script setup name="x-select">
import {Search} from '@element-plus/icons-vue'

let curValue = ref("");
let selected = ref([]);
let list = ref([]);
let source = ref([]);
let pagination = reactive({pageNo: 1, total: 0, pageSize: 10})
let search = ref("")
import {GlobalConfig} from 'core';

const {appConfig} = GlobalConfig();
const dict = appConfig.selectStore && appConfig.selectStore()

const emit = defineEmits(['update:modelValue', 'change'])

let props = defineProps({
  isPage: {
    type: Boolean,
    default: false
  },

  clearable: {
    type: Boolean,
    default: true,
  },

  multiple: {
    type: Boolean,
    default: false //是否多选
  },

  limitCount: {
    type: Number,
    default: 0 //限制多选个数
  },

  modelValue: {
    type: [String, Number, Array]
  },

  count: {
    type: Number,
    default: 50
  },

  name: {
    type: String,
    default: ''
  },

  options: {
    type: [Array, String]
  },

  disabled: {
    type: Boolean,
    default: false,
  },

  labelField: {
    type: [Array, String],
    default: "label",
  },

  valueField: {
    default: "value",
  },

  filter: {
    type: Function
  },

  isDefault: {
    type: Boolean,
    default: false,
  },

  type: {
    type: String,
    default: 'dict'
  },

  cache: {
    type: Boolean,
    default: false
  },

  query: {
    type: Object,
    default: () => {
      return {}
    }
  },
  delay:{
    type: Number,
    default: 0
  }
})

let timeHandler = -1;
const onSearch = (query) => {
  clearTimeout(timeHandler);
  timeHandler = setTimeout(() => {
    let data = [];

    if (Helper.empty(query)) {
      if (source.value.length > props.count) {
        data = source.value.slice(0, props.count);
      } else {
        data = source.value;
      }
      list.value = optionFormat(data);
      return;
    }

    source.value.forEach(s => {
      let labelField = props.labelField;
      if (Array.isArray(labelField)) {
        labelField = props.labelField[0];
      }

      let label = s[labelField];
      if ((!Helper.empty(label) && label.indexOf(query) > -1) || label.toLowerCase().indexOf(query.toLowerCase()) > -1) {
        data.push(s);
      }
    });

    list.value = optionFormat(data);
  }, 100);
}

const onPageSearch = async (pageNo) => {
  pageNo = pageNo ? pageNo : 1;
  pagination.pageNo = pageNo;
  let data = await loadByStores(props.options, search.value, props.query, props.cache, pageNo);
  if (!Helper.emptyJson(data)) {
    let options = data.options;
    if (Helper.emptyArray(options)) {
      options = [];
    }

    let arr = options.filter((v) =>
        source.value.every((val) => val[props.valueField] != v[props.valueField]));

    source.value = source.value.concat(arr);
    list.value = optionFormat(data.options);
    pagination.total = data.total ? data.total : 0;
    pagination.pageSize = data.pageSize ? data.pageSize : 10;
  }
}

/**
 * 处理options array | string 配和type一起处理
 * 若为数组直接渲染，如果为字符串则根据type来判断
 * type 分为 local | dict | remote
 * local 从本地localstorage读取 配合方法为Cache
 * dict 调用dict接口，需要后端约定配合，在config/http配置目录中
 * remote 调用接口方法返回
 * */
const handlerOptions = async (options) => {
  if (!options) {
    return;
  }

  if (props.isPage) {
    await onPageSearch(1);
    handlerValue(curValue.value);
    return;
  }

  let data = [];
  if (typeof options == 'string') {
    if(props.delay > 0) {
      data = await delayLoadByStores(options);
    } else {
      data = await loadByStores(options, "", props.query, props.cache);
    }

  } else {
    data = options;
  }

  if (Helper.emptyArray(data)) {
    Log.error(options);
    data = [];
  }

  source.value = data;

  if (data.length > props.count) {
    data = data.slice(0, props.count);
  }

  list.value = optionFormat(data);
  handlerValue(curValue.value);
}

/**
 * 处理value，当value不在显示范围内，加大寻找力度
 * */
const handlerValue = (value) => {
  selected.value = [];
  if (Helper.empty(value)) {
    return;
  }

  if (Helper.emptyArray(source.value)) {
    return;
  }

  if (props.multiple) {
    if (Helper.emptyArray(value)) {
      return;
    }


    let options = source.value.filter(x => value.indexOf(x[props.valueField] + '') > -1);

    if(! Helper.emptyArray(options)) {
      options.forEach(option => {
        let opValue = option[props.valueField];
        let opLabel = option[props.labelField];
        selected.value.push({label: opLabel, value: opValue + ''})
      })
    }
    return;
  }

  let option = source.value.find(x => x[props.valueField] + '' == value);
  if (option) {
    selected.value = [Object.assign(option, {label: option[props.labelField], value: option[props.valueField] + ''})];
  } else {
    selected.value = [{label: option, value: value + ''}];
  }
}

const optionFormat = (options) => {
  let res = [];
  if (Array.isArray(options)) {
    options.forEach(i => {
      let item = convert(i);
      if (item) {
        res.push(item);
      }
    })
  }

  return res;
}

/**
 * option对象适配
 * @param i
 * @returns {null|{}|boolean}
 */
const convert = (i) => {
  let mapperValue = "value";
  let mapperLabel = "label";
  let item = {};
  let label = "";
  if (props.filter && !props.filter(i)) {
    return false;
  }

  item[mapperValue] = i[props.valueField] + '';
  if (!Array.isArray(props.labelField)) {
    label = i[props.labelField];
    item[mapperLabel] = label;
    return item;
  }

  let labels = [];
  props.labelField.forEach(l => {
    let text = i[l];
    if (!this.empty(text)) {
      labels.push(text);
    }
  })

  label = labels.join("-")
  label = i[props.labelField];
  item[mapperLabel] = label;
  return item;
}

/**
 * 远程接口读取
 * */
const loadByStores = async (options, search, query, cache, pageNo) => {
  return await loadData(options, search, query, cache, pageNo);
}

/**
 * 远程接口读取
 * */
const delayLoadByStores = async (options, pageNo) => {
  return new Promise(resolve => {
    setTimeout(async () => {
      let resp = await loadData(options, "", props.query, props.cache, pageNo);
      resolve(resp);
    }, props.delay)
  })

}

const loadData = async (options, search, query, cache, pageNo) => {
  //todo 验证store是否存在
  if (Helper.empty(dict)) {
    Msg.error("未配置下拉框store，请先检查配置");
    return;
  }

  if (props.isPage) {
    return await dict[options](pageNo, search, query, cache);
  }

  //todo 处理为字典表码情况
  if (props.type == 'dict') {
    if (!dict.get) {
      Msg.error("未在stores中实现获取字典表码的接口");
      return;
    }
    return await dict.get(options, query, cache);
  }

  if (Helper.empty(dict[options])) {
    Msg.error("未实现下拉框的" + options + "，请选实现");
    return;
  }

  let resp = [];
  if (typeof dict[options] == 'function') {
    resp = await dict[options](query, cache);
  } else {
    resp = await dict[options];
  }

  return resp;
}

/**
 * watch  监听
 */
watch(() => props.modelValue, (value) => {
  if(value == null) {
    value = props.multiple ? [] : "";
  }

  if(props.multiple) {
    for (let i=0; i<value.length;i++) {
      value[i] = value[i] + '';
    }
    curValue.value = value;
  } else {
    curValue.value = value + '';
  }
}, {immediate: true, deep: true})

watch(() => curValue.value, (data, oldData) => {
  emit('update:modelValue', data);

  handlerValue(data);
  emit("change", data, oldData, selected.value);
})

watch(() => props.name, (value) => {
  if(Helper.empty(value) || ! props.isPage) {
    return;
  }

  if(props.multiple) {
    let nameArr = value.split(",");
    for (let i=0;i<nameArr.length;i++) {
      let name = nameArr[i];
      let data = curValue.value[i] + '';
      let item = {};
      item[props.labelField] = name;
      item[props.valueField] = data;
      source.value.push(item);
    }
    return;
  }

  let item = {};
  item[props.labelField] = value
  item[props.valueField] = curValue.value
  source.value.push(item);
},{immediate: true})

watch(() => props.options, (value) => {
  handlerOptions(value);
}, {immediate: true, deep: true})


</script>

<style scoped lang="less">
.x-select {

}

:global(.el-select-dropdown__wrap) {
  max-height: 574px;
}
</style>
